home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Amiga Tools 2
/
Amiga Tools 2.iso
/
tools
/
jade
/
lisp
/
compiler.jl
< prev
next >
Wrap
Lisp/Scheme
|
1995-03-09
|
36KB
|
1,068 lines
;;;; compiler.jl -- Simple compiler for Lisp files/forms
;;; Copyright (C) 1993, 1994 John Harper <jsh@ukc.ac.uk>
;;; This file is part of Jade.
;;; Jade is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 2, or (at your option)
;;; any later version.
;;; Jade is distributed in the hope that it will be useful, but
;;; WITHOUT ANY WARRANTY; without even the implied warranty of
;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
;;; GNU General Public License for more details.
;;; You should have received a copy of the GNU General Public License
;;; along with Jade; see the file COPYING. If not, write to
;;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
;;; Notes:
;;;
;;; Instruction Encoding
;;; ====================
;;; Instructions which get an argument (with opcodes of zero up to
;;; `op-last-with-args') encode the type of argument in the low 3 bits
;;; of their opcode (this is why these instructions take up 8 opcodes).
;;; A value of 0 to 5 (inclusive) is the literal argument, value of
;;; 6 means the next byte holds the argument, or a value of 7 says
;;; that the next two bytes are used to encode the argument (in big-
;;; endian form, i.e. first extra byte has the high 8 bits)
;;;
;;; All instructions greater than the `op-last-before-jmps' are branches,
;;; currently only absolute destinations are supported, all branch
;;; instructions encode their destination in the following two bytes (also
;;; in big-endian form).
;;;
;;; Any opcode between `op-last-with-args' and `op-last-before-jmps' is
;;; a straightforward single-byte instruction.
;;;
;;; The machine simulated by lispmach.c is a simple stack-machine, each
;;; call to the byte-code interpreter gets its own stack; the size of
;;; stack needed is calculated by the compiler.
;;;
;;; If you hadn't already noticed I based this on the Emacs version 18
;;; byte-compiler.
;;;
;;; Constants
;;; =========
;;; `defconst' forms have to be used with some care. The compiler assumes
;;; that the value of the constant is always the same, whenever it is
;;; evaluated. It may even be evaluated more than once.
;;;
;;; In general, any symbols declared as constants (by defconst) have their
;;; values set in stone. These values are hard-coded into the compiled
;;; byte-code.
;;;
;;; Also, the value of a constant-symbol is *not* likely to be eq to itself!
;;;
;;; Use constants as you would use macros in C, i.e. to define values which
;;; have to be the same throughout a module. For example, this compiler uses
;;; defconst forms to declare the instruction opcodes.
;;;
;;; If you have doubts about whether or not to use constants -- don't; it may
;;; lead to subtle bugs.
(provide 'compiler)
;; Options
(defvar comp-write-docs nil
"When t all doc-strings are appended to the doc file and replaced with
their position in that file.")
;; Opcodes
(defconst op-call 0x08) ;call (stk[n] stk[n-1] ... stk[0])
; pops n values, replacing the
; function with the result.
(defconst op-push 0x10) ;pushes constant # n
(defconst op-vrefc 0x18) ;pushes val of symbol n (in c-v)
(defconst op-vsetc 0x20) ;sets symbol n (in c-v) to stk[0],
; then pops the stack.
(defconst op-list 0x28) ;makes top n items into a list
(defconst op-bind 0x30) ;bind constant n to stk[0], pops stk
(defconst op-last-with-args 0x37)
(defconst op-vref 0x40) ;replace symbol with it's value
(defconst op-vset 0x41) ;set (sym)stk[0]=stk[1], pops both
(defconst op-fref 0x42) ;similar to vref/vset, but for
(defconst op-fset 0x43) ; function value.
(defconst op-init-bind 0x44) ;initialise a new set of bindings
(defconst op-unbind 0x45) ;unbind all bindings in the top set
(defconst op-dup 0x46) ;duplicate top of stack
(defconst op-swap 0x47) ;swap top two values on stack
(defconst op-pop 0x48) ;pops the stack
(defconst op-nil 0x49) ;pushes nil
(defconst op-t 0x4a) ;pushes t
(defconst op-cons 0x4b)
(defconst op-car 0x4c)
(defconst op-cdr 0x4d)
(defconst op-rplaca 0x4e)
(defconst op-rplacd 0x4f)
(defconst op-nth 0x50)
(defconst op-nthcdr 0x51)
(defconst op-aset 0x52)
(defconst op-aref 0x53)
(defconst op-length 0x54)
(defconst op-eval 0x55)
(defconst op-plus-2 0x56) ;The `-2' on the end means that it
(defconst op-negate 0x57) ; only works on 2 arguments.
(defconst op-minus-2 0x58)
(defconst op-product-2 0x59)
(defconst op-divide-2 0x5a)
(defconst op-mod-2 0x5b)
(defconst op-lognot 0x5c)
(defconst op-not 0x5d)
(defconst op-logior-2 0x5e)
(defconst op-logand-2 0x5f)
(defconst op-equal 0x60)
(defconst op-eq 0x61)
(defconst op-num-eq 0x62)
(defconst op-num-noteq 0x63)
(defconst op-gtthan 0x64)
(defconst op-gethan 0x65)
(defconst op-ltthan 0x66)
(defconst op-lethan 0x67)
(defconst op-inc 0x68)
(defconst op-dec 0x69)
(defconst op-lsh 0x6a)
(defconst op-zerop 0x6b)
(defconst op-null 0x6c)
(defconst op-atom 0x6d)
(defconst op-consp 0x6e)
(defconst op-listp 0x6f)
(defconst op-numberp 0x70)
(defconst op-stringp 0x71)
(defconst op-vectorp 0x72)
(defconst op-catch-kludge 0x73)
(defconst op-throw 0x74)
(defconst op-unwind-pro 0x75)
(defconst op-un-unwind-pro 0x76)
(defconst op-fboundp 0x77)
(defconst op-boundp 0x78)
(defconst op-symbolp 0x79)
(defconst op-get 0x7a)
(defconst op-put 0x7b)
(defconst op-error-pro 0x7c)
(defconst op-signal 0x7d)
(defconst op-return 0x7e)
(defconst op-reverse 0x7f) ;new 12/7/94
(defconst op-nreverse 0x80)
(defconst op-assoc 0x81)
(defconst op-assq 0x82)
(defconst op-rassoc 0x83)
(defconst op-rassq 0x84)
(defconst op-last 0x85)
(defconst op-mapcar 0x86)
(defconst op-mapc 0x87)
(defconst op-member 0x88)
(defconst op-memq 0x89)
(defconst op-delete 0x8a)
(defconst op-delq 0x8b)
(defconst op-delete-if 0x8c)
(defconst op-delete-if-not 0x8d)
(defconst op-copy-sequence 0x8e)
(defconst op-sequencep 0x8f)
(defconst op-functionp 0x90)
(defconst op-special-form-p 0x91)
(defconst op-subrp 0x92)
(defconst op-eql 0x93)
(defconst op-logxor-2 0x94) ;new 23-8-94
(defconst op-set-current-buffer 0xb0)
(defconst op-swap-buffer 0xb1) ;switch to buffer stk[0], stk[0]
; becomes old buffer.
(defconst op-current-buffer 0xb2)
(defconst op-bufferp 0xb3)
(defconst op-markp 0xb4)
(defconst op-windowp 0xb5)
(defconst op-swap-window 0xb6)
(defconst op-last-before-jmps 0xfa)
;; All jmps take two-byte arguments
(defconst op-jmp 0xfb) ;jmp to x
(defconst op-jn 0xfc) ;pop the stack, if nil, jmp x
(defconst op-jt 0xfd) ;pop the stack, if t, jmp x
(defconst op-jnp 0xfe) ;if stk[0] nil, jmp x, else pop
(defconst op-jtp 0xff) ;if stk[0] t, jmp x, else pop
(defconst comp-max-1-byte-arg 5) ;max arg held in 1-byte instruction
(defconst comp-max-2-byte-arg 0xff) ;max arg held in 2-byte instruction
(defconst comp-max-3-byte-arg 0xffff) ;max arg help in 3-byte instruction
;; Environment of this byte code sequence being compiled
(defvar comp-constant-alist '()) ;list of (VALUE . INDEX)
(defvar comp-constant-index 0) ;next free constant index number
(defvar comp-current-stack 0) ;current stack requirement
(defvar comp-max-stack 0) ;highest possible stack
(defvar comp-output nil) ;list of (BYTE . INDEX)
(defvar comp-output-pc 0) ;INDEX of next byte
(defvar comp-macro-env '()) ;alist of (NAME . MACRO-DEF)
(defvar comp-const-env '()) ;alist of (NAME . CONST-DEF)
(defvar comp-top-level-compiled
'(if cond when unless let let* catch unwind-protect error-protect
with-buffer with-window progn prog1 prog2 while and or)
"List of symbols, when the name of the function called by a top-level form
is one of these that form is compiled.")
;;;###autoload
(defun compile-file (file-name)
"Compiles the file of jade-lisp code FILE-NAME into a new file called
`(concat FILE-NAME ?c)' (ie, `foo.jl' => `foo.jlc')."
(interactive "fLisp file to compile:")
(let
(src-file dst-file form
comp-macro-env
comp-const-env)
(when (and (setq src-file (open file-name "r"))
(setq dst-file (open (concat file-name ?c) "w")))
(format dst-file
";;; Source file: %s\n;;; Compiled by %s@%s on %s\n;;; Jade %d.%d\n"
file-name (user-login-name) (system-name) (current-time-string)
(major-version-number) (minor-version-number))
(error-protect
(unwind-protect
(let
(form)
(message (concat "Compiling " file-name "...") t)
(while (not (file-eof-p src-file))
(when (setq form (read src-file))
(cond
((memq (car form) '(defun defmacro defvar defconst require))
(setq form (comp-compile-top-form form)))
((memq (car form) comp-top-level-compiled)
;; Compile this form
(setq form (compile-form form))))
(when form
(print form dst-file)
(write dst-file ?\n)))))
(close dst-file)
(close src-file))
(error
;; Be sure to remove any partially written dst-file. Also, signal
;; the error again so that the user sees it.
(let
((fname (concat file-name ?c)))
(when (file-exists-p fname)
(delete-file fname)))
;; Hack to signal error without entering the debugger (again)
(throw 'error error-info)))
t)))
;;;###autoload
(defun compile-directory (dir-name &optional force-p exclude-list)
"Compiles all jade-lisp files in the directory DIRECTORY-NAME whose object
files are either older than their source file or don't exist. If FORCE-P
is non-nil every lisp file is recompiled.
EXCLUDE-LIST is a list of files which shouldn't be compiled."
(interactive "DDirectory of Lisp files to compile:\nP")
(let
((dir (directory-files dir-name)))
(while (consp dir)
(when (and (regexp-match "\\.jl$" (car dir))
(null (member (car dir) exclude-list)))
(let*
((file (file-name-concat dir-name (car dir)))
(cfile (concat file ?c)))
(when (file-newer-than-file-p file cfile)
(compile-file file))))
(setq dir (cdr dir)))
t))
(defvar compile-lib-exclude-list
'("autoload.jl"))
;;;###autoload
(defun compile-lisp-lib (&optional force-p)
"Recompile all out of date files in the lisp library directory. If FORCE-P
is non-nil it's as though all files were out of date.
This makes sure that all doc strings are written to their special file and
that files which shouldn't be compiled aren't."
(interactive "P")
(let
((comp-write-docs t))
(compile-directory lisp-lib-dir force-p compile-lib-exclude-list)))
(put 'compile-error 'error-message "Compilation mishap")
(defun comp-error (&rest data)
(signal 'compile-error data))
;; Compile a form which occurred at the `top-level' into a byte code form.
;; defuns, defmacros, defvars, etc... are treated specially.
;; require forms are evaluated before being output uncompiled; this is so
;; any macros are brought in before they're used.
(defun comp-compile-top-form (form)
(let
((fun (car form)))
(cond
((eq fun 'defun)
(let
((tmp (assq (nth 1 form) comp-macro-env)))
(when tmp
(rplaca tmp nil)
(rplacd tmp nil)))
(cons 'defun
(cons (nth 1 form)
(cdr (comp-compile-lambda (cons 'lambda (nthcdr 2 form)))))))
((eq fun 'defmacro)
(let
((code (comp-compile-lambda (cons 'lambda (nthcdr 2 form))))
(tmp (assq (nth 1 form) comp-macro-env)))
(if tmp
(rplacd tmp code)
(setq comp-macro-env (cons (cons (nth 1 form) code) comp-macro-env)))
(cons 'defmacro (cons (nth 1 form) (cdr code)))))
((eq fun 'defconst)
(let
((value (eval (nth 2 form)))
(doc (nth 3 form)))
(when (and comp-write-docs (stringp doc))
(rplaca (nthcdr 3 form) (add-doc-string doc)))
(setq comp-const-env (cons (cons (nth 1 form) value) comp-const-env)))
form)
((eq fun 'defvar)
(let
((doc (nth 3 form)))
(when (and comp-write-docs (stringp doc))
(rplaca (nthcdr 3 form) (add-doc-string doc))))
form)
((eq fun 'require)
(eval form)
form)
(t
(comp-error "Shouldn't have got here!")))))
;;;###autoload
(defun compile-form (form)
"Compile the Lisp form FORM into a byte code form."
(let
(comp-constant-alist
(comp-constant-index 0)
(comp-current-stack 0)
(comp-max-stack 0)
comp-output
(comp-output-pc 0))
(comp-compile-form form)
(when comp-output
(list 'jade-byte-code (comp-make-code-string) (comp-make-const-vec)
comp-max-stack))))
;; Turn the alist of byte codes into a string
(defun comp-make-code-string ()
(let
((code-string (make-string comp-output-pc ?*))
(data comp-output))
(while (consp data)
(aset code-string (cdr (car data)) (car (car data)))
(setq data (cdr data)))
code-string))
;; Turn the alist of constants into a vector
(defun comp-make-const-vec ()
(let
((vec (make-vector comp-constant-index))
(consts comp-constant-alist))
(while (consp consts)
(aset vec (cdr (car consts)) (car (car consts)))
(setq consts (cdr consts)))
vec))
;; Increment the current stack size, setting the maximum stack size if
;; necessary
(defun comp-inc-stack ()
(when (> (setq comp-current-stack (1+ comp-current-stack)) comp-max-stack)
(setq comp-max-stack comp-current-stack)))
;; Decrement the current stack usage
(defmacro comp-dec-stack (&optional n)
(list 'setq 'comp-current-stack
(if n
(list '- 'comp-current-stack n)
(list '1- 'comp-current-stack))))
;; Compile one form so that its value ends up on the stack when interpreted
(defun comp-compile-form (form)
(cond
((eq form nil)
(comp-write-op op-nil)
(comp-inc-stack))
((eq form t)
(comp-write-op op-t)
(comp-inc-stack))
((symbolp form)
(let
(val)
(cond
((const-variable-p form)
;; A constant already interned
(comp-write-op op-push (comp-add-constant (symbol-value form)))
(comp-inc-stack))
((setq val (assq form comp-const-env))
;; A constant from this file
(comp-compile-form (cdr val)))
(t
;; Not a constant
(comp-write-op op-vrefc (comp-add-constant form))
(comp-inc-stack)))))
((consp form)
(let
(fun)
(if (and (symbolp (car form)) (setq fun (get (car form) 'compile-fun)))
(funcall fun form)
(setq form (macroexpand form comp-macro-env))
(if (and (symbolp (car form))
(setq fun (get (car form) 'compile-fun)))
(funcall fun form)
(setq fun (car form))
(cond
((symbolp fun)
(comp-compile-constant fun))
((and (consp fun) (eq (car fun) 'lambda))
(comp-compile-constant (comp-compile-lambda fun)))
(t
(comp-error "Bad function name" fun)))
(setq form (cdr form))
(let
((i 0))
(while (consp form)
(comp-compile-form (car form))
(setq i (1+ i)
form (cdr form)))
(comp-write-op op-call i)
(comp-dec-stack i))))))
(t
(comp-compile-constant form))))
;; Push a constant onto the stack
(defun comp-compile-constant (form)
(comp-write-op op-push (comp-add-constant form))
(comp-inc-stack))
;; Put a constant into the alist of constants, returning its index number.
;; It won't be added twice if it's already there.
(defun comp-add-constant (const)
(unless (cdr (assoc const comp-constant-alist))
(setq comp-constant-alist (cons (cons const comp-constant-index)
comp-constant-alist)
comp-constant-index (1+ comp-constant-index))
(1- comp-constant-index)))
;; Compile a list of forms, the last form's evaluated value is left on
;; the stack. If the list is empty nil is pushed.
(defun comp-compile-body (body)
(if (null body)
(progn
(comp-write-op op-nil)
(comp-inc-stack))
(while (consp body)
(comp-compile-form (car body))
(when (cdr body)
(comp-write-op op-pop)
(comp-dec-stack))
(setq body (cdr body)))))
;; From LIST, `(lambda (ARGS) [DOC-STRING] BODY ...)' returns a new list of,
;; `(lambda (ARGS) [DOC-STRING] (jade-byte-code ...))'
(defun comp-compile-lambda (list)
(let
((body (nthcdr 2 list))
new-head)
(cond
((stringp (car body))
(setq body (cdr body)
new-head (list 'lambda (nth 1 list)
(if comp-write-docs
(add-doc-string (nth 2 list))
(nth 2 list)))))
(t
(setq new-head (list 'lambda (nth 1 list)))))
;; Check for an `(interactive ...)' declaration; it doesn't get compiled
(when (eq (car (car body)) 'interactive)
(setq new-head (nconc new-head (list (car body)))
body (cdr body)))
(nconc new-head (cons (compile-form (cons 'progn body)) nil))))
;; Managing the output code
;; Return a new label
(defmacro comp-make-label ()
;; a label is, (PC-OF-LABEL . (LIST-OF-REFERENCES))
'(cons nil nil))
;; Output a branch instruction to the label LABEL, if LABEL has not been
;; located yet this branch is recorded for later backpatching.
(defun comp-compile-jmp (opcode label)
(comp-byte-out opcode)
(cond
((numberp (car label))
;; we know the final offset of this label so use it
(comp-byte-out (lsh (car label) -8))
(comp-byte-out (logand (car label) 0xff)))
(t
;; offset unknown, show we need it patched in later
(rplacd label (cons comp-output-pc (cdr label)))
(setq comp-output-pc (+ comp-output-pc 2)))))
;; Set the address of the label LABEL, any references to it are patched
;; with its address.
(defun comp-set-label (label)
(when (> comp-output-pc comp-max-3-byte-arg)
(comp-error "Jump destination overflow!"))
(rplaca label comp-output-pc)
(setq label (cdr label))
(while (consp label)
(setq comp-output (cons (cons (lsh comp-output-pc -8) (car label))
(cons (cons (logand comp-output-pc 0xff)
(1+ (car label)))
comp-output))
label (cdr label))))
;; Output one opcode and its optional argument
(defun comp-write-op (opcode &optional arg)
(cond
((null arg)
(comp-byte-out opcode))
((<= arg comp-max-1-byte-arg)
(comp-byte-out (+ opcode arg)))
((<= arg comp-max-2-byte-arg)
;; 2-byte instruction
(comp-byte-out (+ opcode 6))
(comp-byte-out arg))
((<= arg comp-max-3-byte-arg)
;; 3-byte instruction
(comp-byte-out (+ opcode 7))
(comp-byte-out (lsh arg -8))
(comp-byte-out (logand arg 0xff)))
(t
(comp-error "Opcode overflow!"))))
;; Output one byte
(defun comp-byte-out (byte)
(setq comp-output (cons (cons byte comp-output-pc) comp-output)
comp-output-pc (1+ comp-output-pc)))
;; Functions which compile non-standard functions (ie special-forms)
(put 'if 'compile-fun 'comp-compile-if)
(defun comp-compile-if (form)
(comp-compile-form (nth 1 form))
(if (= (length form) 3)
(let*
((end-label (comp-make-label)))
(comp-compile-jmp op-jnp end-label)
(comp-dec-stack)
(comp-compile-form (nth 2 form))
(comp-set-label end-label))
(let*
((end-label (comp-make-label))
(else-label (comp-make-label)))
(comp-compile-jmp op-jn else-label)
(comp-dec-stack)
(comp-compile-form (nth 2 form))
(comp-compile-jmp op-jmp end-label)
(comp-set-label else-label)
(comp-dec-stack)
(comp-compile-body (nthcdr 3 form))
(comp-set-label end-label))))
(put 'when 'compile-fun 'comp-compile-when)
(defun comp-compile-when (form)
(comp-compile-form (nth 1 form))
(let
((end-label (comp-make-label)))
(comp-compile-jmp op-jnp end-label)
(comp-dec-stack)
(comp-compile-body (nthcdr 2 form))
(comp-set-label end-label)))
(put 'unless 'compile-fun 'comp-compile-unless)
(defun comp-compile-unless (form)
(comp-compile-form (nth 1 form))
(let
((end-label (comp-make-label)))
(comp-compile-jmp op-jtp end-label)
(comp-dec-stack)
(comp-compile-body (nthcdr 2 form))
(comp-set-label end-label)))
(put 'quote 'compile-fun 'comp-compile-quote)
(defun comp-compile-quote (form)
(comp-compile-constant (car (cdr form))))
(put 'function 'compile-fun 'comp-compile-function)
(defun comp-compile-function (form)
(setq form (car (cdr form)))
(if (symbolp form)
(comp-compile-constant form)
(comp-compile-constant (comp-compile-lambda form))))
(put 'while 'compile-fun 'comp-compile-while)
(defun comp-compile-while (form)
(let*
((tst-label (comp-make-label))
(end-label (comp-make-label)))
(comp-set-label tst-label)
(comp-compile-form (nth 1 form))
(comp-compile-jmp op-jnp end-label)
(comp-dec-stack)
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-pop)
(comp-dec-stack)
(comp-compile-jmp op-jmp tst-label)
(comp-set-label end-label)
(comp-inc-stack)))
(put 'progn 'compile-fun 'comp-compile-progn)
(defun comp-compile-progn (form)
(comp-compile-body (cdr form)))
(put 'prog1 'compile-fun 'comp-compile-prog1)
(defun comp-compile-prog1 (form)
(comp-compile-form (nth 1 form))
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-pop)
(comp-dec-stack))
(put 'prog2 'compile-fun 'comp-compile-prog2)
(defun comp-compile-prog2 (form)
(comp-compile-form (nth 1 form))
(comp-write-op op-pop)
(comp-dec-stack)
(comp-compile-form (nth 2 form))
(comp-compile-body (nthcdr 3 form))
(comp-write-op op-pop)
(comp-dec-stack))
(put 'setq 'compile-fun 'comp-compile-setq)
(defun comp-compile-setq (form)
(setq form (cdr form))
(while (and (consp form) (consp (cdr form)))
(comp-compile-form (car (cdr form)))
(unless (consp (nthcdr 2 form))
(comp-write-op op-dup)
(comp-inc-stack))
(comp-write-op op-vsetc (comp-add-constant (car form)))
(comp-dec-stack)
(setq form (nthcdr 2 form))))
(put 'set 'compile-fun 'comp-compile-set)
(defun comp-compile-set (form)
(comp-compile-form (nth 2 form))
(comp-write-op op-dup)
(comp-inc-stack)
(comp-compile-form (nth 1 form))
(comp-write-op op-vset)
(comp-dec-stack 2))
(put 'fset 'compile-fun 'comp-compile-fset)
(defun comp-compile-fset (form)
(comp-compile-form (nth 2 form))
(comp-write-op op-dup)
(comp-inc-stack)
(comp-compile-form (nth 1 form))
(comp-write-op op-fset)
(comp-dec-stack 2))
(put 'let* 'compile-fun 'comp-compile-let*)
(defun comp-compile-let* (form)
(let
((list (car (cdr form))))
(comp-write-op op-init-bind)
(while (consp list)
(cond
((consp (car list))
(let
((tmp (car list)))
(comp-compile-body (cdr tmp))
(comp-write-op op-bind (comp-add-constant (car tmp)))))
(t
(comp-write-op op-nil)
(comp-inc-stack)
(comp-write-op op-bind (comp-add-constant (car list)))))
(comp-dec-stack)
(setq list (cdr list)))
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-unbind)))
(put 'let 'compile-fun 'comp-compile-let)
(defun comp-compile-let (form)
(let
((list (car (cdr form)))
(sym-stk nil))
(comp-write-op op-init-bind)
(while (consp list)
(cond
((consp (car list))
(setq sym-stk (cons (car (car list)) sym-stk))
(comp-compile-body (cdr (car list))))
(t
(setq sym-stk (cons (car list) sym-stk))
(comp-write-op op-nil)
(comp-inc-stack)))
(setq list (cdr list)))
(while (consp sym-stk)
(comp-write-op op-bind (comp-add-constant (car sym-stk)))
(comp-dec-stack)
(setq sym-stk (cdr sym-stk)))
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-unbind)))
(put 'defun 'compile-fun 'comp-compile-defun)
(defun comp-compile-defun (form)
(comp-compile-constant (nth 1 form))
(comp-write-op op-dup)
(comp-inc-stack)
(comp-compile-constant (comp-compile-lambda (cons 'lambda (nthcdr 2 form))))
(comp-write-op op-swap)
(comp-write-op op-fset)
(comp-dec-stack 2))
(put 'defmacro 'compile-fun 'comp-compile-defmacro)
(defun comp-compile-defmacro (form)
(comp-compile-constant (nth 1 form))
(comp-write-op op-dup)
(comp-inc-stack)
(comp-compile-constant (cons 'macro (comp-compile-lambda (cons 'lambda (nthcdr 2 form)))))
(comp-write-op op-swap)
(comp-write-op op-fset)
(comp-dec-stack 2))
(put 'cond 'compile-fun 'comp-compile-cond)
(defun comp-compile-cond (form)
(let
((end-label (comp-make-label)))
(setq form (cdr form))
(while (consp form)
(let
((subl (car form))
(next-label (comp-make-label)))
(comp-compile-form (car subl))
(comp-dec-stack)
(cond
((consp (cdr subl))
(comp-compile-jmp op-jn next-label)
(comp-compile-body (cdr subl))
(comp-dec-stack)
(comp-compile-jmp op-jmp end-label)
(comp-set-label next-label))
(t
(comp-compile-jmp op-jtp end-label)))
(setq form (cdr form))))
(comp-write-op op-nil)
(comp-inc-stack)
(comp-set-label end-label)))
(put 'or 'compile-fun 'comp-compile-or)
(defun comp-compile-or (form)
(let
((end-label (comp-make-label)))
(setq form (cdr form))
(while (consp form)
(comp-compile-form (car form))
(comp-dec-stack)
(when (cdr form)
(comp-compile-jmp op-jtp end-label))
(setq form (cdr form)))
(comp-inc-stack)
(comp-set-label end-label)))
(put 'and 'compile-fun 'comp-compile-and)
(defun comp-compile-and (form)
(let
((end-label (comp-make-label)))
(setq form (cdr form))
(while (consp form)
(comp-compile-form (car form))
(comp-dec-stack)
(when (cdr form)
(comp-compile-jmp op-jnp end-label))
(setq form (cdr form)))
(comp-inc-stack)
(comp-set-label end-label)))
(put 'catch 'compile-fun 'comp-compile-catch)
(defun comp-compile-catch (form)
(comp-compile-constant (compile-form (cons 'progn (nthcdr 2 form))))
(comp-compile-constant (nth 1 form))
(comp-write-op op-catch-kludge)
(comp-dec-stack))
(put 'unwind-protect 'compile-fun 'comp-compile-unwind-pro)
(defun comp-compile-unwind-pro (form)
(comp-compile-constant (compile-form (cons 'progn (nthcdr 2 form))))
(comp-write-op op-unwind-pro)
(comp-dec-stack)
(comp-compile-form (nth 1 form))
(comp-write-op op-un-unwind-pro))
(put 'error-protect 'compile-fun 'comp-compile-error-protect)
(defun comp-compile-error-protect (form)
(let
((i 0))
(setq form (cdr form))
(unless (consp form)
(comp-error "No FORM to `error-protect'" form))
(comp-compile-constant (compile-form (car form)))
(setq form (cdr form))
(while (consp form)
(let
((handler (car form)))
(unless (consp handler)
(comp-error "Badly formed handler to `error-protect'" form))
(comp-compile-constant (list (car handler)
(compile-form (cons 'progn
(cdr handler)))))
(setq form (cdr form)
i (1+ i))))
(comp-compile-constant (1+ i))
(comp-write-op op-error-pro)
(comp-dec-stack i)))
(put 'list 'compile-fun 'comp-compile-list)
(defun comp-compile-list (form)
(let
((count 0))
(setq form (cdr form))
(while (consp form)
(comp-compile-form (car form))
(setq
count (1+ count)
form (cdr form)))
(comp-write-op op-list count)
(comp-dec-stack (1- count))))
(put 'with-buffer 'compile-fun 'comp-compile-with-buffer)
(defun comp-compile-with-buffer (form)
(comp-compile-form (nth 1 form))
(comp-write-op op-swap-buffer)
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-swap)
(comp-write-op op-swap-buffer)
(comp-write-op op-pop)
(comp-dec-stack))
(put 'with-window 'compile-fun 'comp-compile-with-window)
(defun comp-compile-with-window (form)
(comp-compile-form (nth 1 form))
(comp-write-op op-swap-window)
(comp-compile-body (nthcdr 2 form))
(comp-write-op op-swap)
(comp-write-op op-swap-window)
(comp-write-op op-pop)
(comp-dec-stack))
(put '- 'compile-fun 'comp-compile-minus)
(put '- 'compile-opcode op-minus-2)
(defun comp-compile-minus (form)
(if (/= (length form) 2)
(comp-compile-binary-op form)
(comp-compile-form (car (cdr form)))
(comp-write-op op-negate)))
;; Instruction with no arguments
(defun comp-compile-0-args (form)
(comp-write-op (get (car form) 'compile-opcode) 0)
(comp-inc-stack))
;; Instruction taking 1 arg on the stack
(defun comp-compile-1-args (form)
(comp-compile-form (nth 1 form))
(comp-write-op (get (car form) 'compile-opcode) 0))
;; Instruction taking 2 args on the stack
(defun comp-compile-2-args (form)
(comp-compile-form (nth 1 form))
(comp-compile-form (nth 2 form))
(comp-write-op (get (car form) 'compile-opcode) 0)
(comp-dec-stack))
;; Instruction taking 3 args on the stack
(defun comp-compile-3-args (form)
(comp-compile-form (nth 1 form))
(comp-compile-form (nth 2 form))
(comp-compile-form (nth 3 form))
(comp-write-op (get (car form) 'compile-opcode) 0)
(comp-dec-stack 2))
;; Compile a form `(OP ARG1 ARG2 ARG3 ...)' into as many two argument
;; instructions as needed (PUSH ARG1; PUSH ARG2; OP; PUSH ARG3; OP; ...)
(defun comp-compile-binary-op (form)
(let
((opcode (get (car form) 'compile-opcode)))
(setq form (cdr form))
(unless (>= (length form) 2)
(comp-error "Too few args to binary operator" form))
(comp-compile-form (car form))
(setq form (cdr form))
(while (consp form)
(comp-compile-form (car form))
(comp-write-op opcode)
(comp-dec-stack)
(setq form (cdr form)))))
;; Opcode properties for the generic instructions, in a progn for compiled
;; speed
(progn
(put 'cons 'compile-fun 'comp-compile-2-args)
(put 'cons 'compile-opcode op-cons)
(put 'car 'compile-fun 'comp-compile-1-args)
(put 'car 'compile-opcode op-car)
(put 'cdr 'compile-fun 'comp-compile-1-args)
(put 'cdr 'compile-opcode op-cdr)
(put 'rplaca 'compile-fun 'comp-compile-2-args)
(put 'rplaca 'compile-opcode op-rplaca)
(put 'rplacd 'compile-fun 'comp-compile-2-args)
(put 'rplacd 'compile-opcode op-rplacd)
(put 'nth 'compile-fun 'comp-compile-2-args)
(put 'nth 'compile-opcode op-nth)
(put 'nthcdr 'compile-fun 'comp-compile-2-args)
(put 'nthcdr 'compile-opcode op-nthcdr)
(put 'aset 'compile-fun 'comp-compile-3-args)
(put 'aset 'compile-opcode op-aset)
(put 'aref 'compile-fun 'comp-compile-2-args)
(put 'aref 'compile-opcode op-aref)
(put 'length 'compile-fun 'comp-compile-1-args)
(put 'length 'compile-opcode op-length)
(put 'eval 'compile-fun 'comp-compile-1-args)
(put 'eval 'compile-opcode op-eval)
(put '+ 'compile-fun 'comp-compile-binary-op)
(put '+ 'compile-opcode op-plus-2)
(put '* 'compile-fun 'comp-compile-binary-op)
(put '* 'compile-opcode op-product-2)
(put '/ 'compile-fun 'comp-compile-binary-op)
(put '/ 'compile-opcode op-divide-2)
(put '% 'compile-fun 'comp-compile-binary-op)
(put '% 'compile-opcode op-mod-2)
(put 'lognot 'compile-fun 'comp-compile-1-args)
(put 'lognot 'compile-opcode op-lognot)
(put 'not 'compile-fun 'comp-compile-1-args)
(put 'not 'compile-opcode op-not)
(put 'logior 'compile-fun 'comp-compile-binary-op)
(put 'logior 'compile-opcode op-logior-2)
(put 'logxor 'compile-fun 'comp-compile-binary-op)
(put 'logxor 'compile-opcode op-logxor-2)
(put 'logand 'compile-fun 'comp-compile-binary-op)
(put 'logand 'compile-opcode op-logand-2)
(put 'equal 'compile-fun 'comp-compile-2-args)
(put 'equal 'compile-opcode op-equal)
(put 'eq 'compile-fun 'comp-compile-2-args)
(put 'eq 'compile-opcode op-eq)
(put '= 'compile-fun 'comp-compile-2-args)
(put '= 'compile-opcode op-num-eq)
(put '/= 'compile-fun 'comp-compile-2-args)
(put '/= 'compile-opcode op-num-noteq)
(put '> 'compile-fun 'comp-compile-2-args)
(put '> 'compile-opcode op-gtthan)
(put '< 'compile-fun 'comp-compile-2-args)
(put '< 'compile-opcode op-ltthan)
(put '>= 'compile-fun 'comp-compile-2-args)
(put '>= 'compile-opcode op-gethan)
(put '<= 'compile-fun 'comp-compile-2-args)
(put '<= 'compile-opcode op-lethan)
(put '1+ 'compile-fun 'comp-compile-1-args)
(put '1+ 'compile-opcode op-inc)
(put '1- 'compile-fun 'comp-compile-1-args)
(put '1- 'compile-opcode op-dec)
(put 'lsh 'compile-fun 'comp-compile-2-args)
(put 'lsh 'compile-opcode op-lsh)
(put 'zerop 'compile-fun 'comp-compile-1-args)
(put 'zerop 'compile-opcode op-zerop)
(put 'null 'compile-fun 'comp-compile-1-args)
(put 'null 'compile-opcode op-null)
(put 'atom 'compile-fun 'comp-compile-1-args)
(put 'atom 'compile-opcode op-atom)
(put 'consp 'compile-fun 'comp-compile-1-args)
(put 'consp 'compile-opcode op-consp)
(put 'listp 'compile-fun 'comp-compile-1-args)
(put 'listp 'compile-opcode op-listp)
(put 'numberp 'compile-fun 'comp-compile-1-args)
(put 'numberp 'compile-opcode op-numberp)
(put 'stringp 'compile-fun 'comp-compile-1-args)
(put 'stringp 'compile-opcode op-stringp)
(put 'vectorp 'compile-fun 'comp-compile-1-args)
(put 'vectorp 'compile-opcode op-vectorp)
(put 'throw 'compile-fun 'comp-compile-2-args)
(put 'throw 'compile-opcode op-throw)
(put 'fboundp 'compile-fun 'comp-compile-1-args)
(put 'fboundp 'compile-opcode op-fboundp)
(put 'boundp 'compile-fun 'comp-compile-1-args)
(put 'boundp 'compile-opcode op-boundp)
(put 'symbolp 'compile-fun 'comp-compile-1-args)
(put 'symbolp 'compile-opcode op-symbolp)
(put 'get 'compile-fun 'comp-compile-2-args)
(put 'get 'compile-opcode op-get)
(put 'put 'compile-fun 'comp-compile-3-args)
(put 'put 'compile-opcode op-put)
(put 'signal 'compile-fun 'comp-compile-2-args)
(put 'signal 'compile-opcode op-signal)
(put 'return 'compile-fun 'comp-compile-1-args)
(put 'return 'compile-opcode op-return)
(put 'reverse 'compile-fun 'comp-compile-1-args) ; new 12/7/94
(put 'reverse 'compile-opcode op-reverse)
(put 'nreverse 'compile-fun 'comp-compile-1-args)
(put 'nreverse 'compile-opcode op-nreverse)
(put 'assoc 'compile-fun 'comp-compile-2-args)
(put 'assoc 'compile-opcode op-assoc)
(put 'assq 'compile-fun 'comp-compile-2-args)
(put 'assq 'compile-opcode op-assq)
(put 'rassoc 'compile-fun 'comp-compile-2-args)
(put 'rassoc 'compile-opcode op-rassoc)
(put 'rassq 'compile-fun 'comp-compile-2-args)
(put 'rassq 'compile-opcode op-rassq)
(put 'last 'compile-fun 'comp-compile-2-args)
(put 'last 'compile-opcode op-last)
(put 'mapcar 'compile-fun 'comp-compile-2-args)
(put 'mapcar 'compile-opcode op-mapcar)
(put 'mapc 'compile-fun 'comp-compile-2-args)
(put 'mapc 'compile-opcode op-mapc)
(put 'member 'compile-fun 'comp-compile-2-args)
(put 'member 'compile-opcode op-member)
(put 'memq 'compile-fun 'comp-compile-2-args)
(put 'memq 'compile-opcode op-memq)
(put 'delete 'compile-fun 'comp-compile-2-args)
(put 'delete 'compile-opcode op-delete)
(put 'delq 'compile-fun 'comp-compile-2-args)
(put 'delq 'compile-opcode op-delq)
(put 'delete-if 'compile-fun 'comp-compile-2-args)
(put 'delete-if 'compile-opcode op-delete-if)
(put 'delete-if-not 'compile-fun 'comp-compile-2-args)
(put 'delete-if-not 'compile-opcode op-delete-if-not)
(put 'copy-sequence 'compile-fun 'comp-compile-1-args)
(put 'copy-sequence 'compile-opcode op-copy-sequence)
(put 'sequencep 'compile-fun 'comp-compile-1-args)
(put 'sequencep 'compile-opcode op-sequencep)
(put 'functionp 'compile-fun 'comp-compile-1-args)
(put 'functionp 'compile-opcode op-functionp)
(put 'special-form-p 'compile-fun 'comp-compile-1-args)
(put 'special-form-p 'compile-opcode op-special-form-p)
(put 'subrp 'compile-fun 'comp-compile-1-args)
(put 'subrp 'compile-opcode op-subrp)
(put 'eql 'compile-fun 'comp-compile-2-args)
(put 'eql 'compile-opcode op-eql)
(put 'set-current-buffer 'compile-fun 'comp-compile-2-args)
(put 'set-current-buffer 'compile-opcode op-set-current-buffer)
(put 'current-buffer 'compile-fun 'comp-compile-1-args)
(put 'current-buffer 'compile-opcode op-current-buffer)
(put 'bufferp 'compile-fun 'comp-compile-1-args)
(put 'bufferp 'compile-opcode op-bufferp)
(put 'markp 'compile-fun 'comp-compile-1-args)
(put 'markp 'compile-opcode op-markp)
(put 'windowp 'compile-fun 'comp-compile-1-args)
(put 'windowp 'compile-opcode op-windowp))